package com.example.venue_reservation_service.service.impl;

import cn.hutool.core.lang.UUID;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.venue_reservation_service.domain.*;
import com.example.venue_reservation_service.dto.*;
import com.example.venue_reservation_service.exception.ImageValidateException;
import com.example.venue_reservation_service.exception.MinioRemoveException;
import com.example.venue_reservation_service.exception.MinioUploadException;
import com.example.venue_reservation_service.mapper.AdminMapper;
import com.example.venue_reservation_service.service.*;
import com.example.venue_reservation_service.mapper.UserMapper;
import com.example.venue_reservation_service.utils.*;
import com.example.venue_reservation_service.vo.LoginVo;
import com.example.venue_reservation_service.vo.QueryVO;
import com.example.venue_reservation_service.vo.Result;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import jakarta.annotation.Resource;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* @author 31586
* @description 针对表【venue_user】的数据库操作Service实现
* @createDate 2024-11-12 21:43:55
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{
    @Resource
    private BalanceService balanceService;

    @Resource
    private PaymentService paymentService;

    @Resource
    private RestTemplate restTemplate;

    @Value("${weixin.app_id}")
    private String APP_ID;

    @Value("${weixin.secret}")
    private String SECRET;

    @Resource
    private SchoolService schoolService;

    @Resource
    private AdminMapper adminMapper;

    @Resource
    private RedisUtil redisUtil;

    @Value("${spring.application.name}")
    private String name;

    @Resource
    private MinioUtil minioUtil;

    @Resource
    private EmailService emailService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Value("${access.url}")
    private String imgPrefix;

    private static final String LOGIN_LOCK_PREFIX = "login_lock:";
    private static final int MAX_LOGIN_ATTEMPTS = 3;
    private static final long LOCK_DURATION_MS = 5 * 60 * 1000; // 5 minutes in milliseconds
    @Override
    public Result getOpenId(String code) {
        Map<String, String> vo = restTemplate.getForObject("https://api.weixin.qq.com/sns/jscode2session?"+
                        "appid=" + APP_ID +//小程序ID
                        "&secret=" + SECRET + //小程序密钥
                        "&js_code=" + code + //前端 传入 code
                        "&grant_type=authorization_code", Map.class);
        // 获取openid
        String openid = vo.get("openid");
        System.out.println(openid);
        if(openid == null || "".equals(openid)){
            return Result.fail().message("登录失败");
        }
        User user = getOne(Wrappers.<User>lambdaQuery().eq(User::getOpenId, openid));
        LoginVo loginVo = new LoginVo();
        loginVo.setIsFirst(0);
        if(Optional.ofNullable(user).isEmpty()){
            // 如果当前用户信息不存在，进行注册
            user = new User();
            // 生成默认用户名
            user.setUsername("用户" + System.currentTimeMillis());
            // 设置OpenId，作唯一标识
            user.setOpenId(openid);
            // 表示普通用户
            user.setType(0);
            // 设置用户账号（系统生成）
            user.setAccount(PasswordGenerator.generateAccount());
            // 设置用户初始密码
            user.setAccessCode(PasswordGenerator.generatePassword());
            user.setRegisterTime(LocalDateTime.now());
            user.setIsUpload(0);
            user.setAvatar("");
            save(user);
            // 添加新的账户余额信息
            Balance balance = new Balance(0.0, user.getId());
            balanceService.save(balance);
            loginVo.setIsFirst(1);
            loginVo.setAccessCode(user.getAccessCode());
        }
        // 获取用户账号余额数据
        Double money = balanceService.getOne(Wrappers.<Balance>lambdaQuery().eq(Balance::getUserId, user.getId()))
                .getBalance();
        if(user.getIsUpload() == 1){
            user.setAvatar(imgPrefix + user.getAvatar());
        }
        user.setBalance(money);
        loginVo.setUser(user);
        loginVo.setToken(JwtUtil.createToken(user.getId()));
        return Result.ok(loginVo).message("登录成功");
    }

    @Override
    public Result adminLogin(AdminDTO dto) {
        String account = dto.getAccount();
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getAccount, account);
        User user = getOne(wrapper);
        if (user == null) { // 修正：避免重复查询
            return Result.fail().message("用户账号不存在");
        }

        String password = dto.getPasswd();
        String redisKey = LOGIN_LOCK_PREFIX + account; // 使用固定前缀

        if (!user.getAccessCode().equals(password)) {
            // 处理密码错误的情况
            if (redisTemplate.hasKey(redisKey)) {
                String[] parts = String.valueOf(redisTemplate.opsForValue().get(redisKey)).split("-");
                int failCount = Integer.parseInt(parts[0]);
                long firstFailTime = Long.parseLong(parts[1]);

                // 错误次数已达上限
                if (failCount >= MAX_LOGIN_ATTEMPTS) {
                    long lockTime = LOCK_DURATION_MS; // 5分钟毫秒数
                    long remainMillis = lockTime - (System.currentTimeMillis() - firstFailTime);
                    if (remainMillis > 0) {
                        return Result.fail().message("该账号已被锁定，请在 " + (remainMillis / 1000) + " 秒后重新登录");
                    } else {
                        // 锁定已过期，重置计数器
                        redisTemplate.delete(redisKey);
                    }
                }
            }

            // 更新错误计数器（包含首次错误情况）
            int newCount = 1;
            long timestamp = System.currentTimeMillis();
            if (redisTemplate.hasKey(redisKey)) {
                String[] parts = String.valueOf(redisTemplate.opsForValue().get(redisKey)).split("-");
                newCount = Integer.parseInt(parts[0]) + 1;
                timestamp = Long.parseLong(parts[1]); // 保持第一次错误时间
            }

            // 更新Redis，仅首次设置过期时间
            String value = newCount + "-" + timestamp;
            if (newCount == 1) {
                redisTemplate.opsForValue().set(redisKey, value, Duration.ofMinutes(5));
            } else {
                redisTemplate.opsForValue().set(redisKey, value); // 更新次数但保留原TTL
            }

            return Result.fail().message("账号密码不匹配");
        }

        // 密码正确，登录成功
        // 清除登录错误计数
        if (redisTemplate.hasKey(redisKey)) {
            redisTemplate.delete(redisKey);
        }

        user.setAccessCode("");
        if (user.getIsUpload() == 1) {
            user.setAvatar(imgPrefix + user.getAvatar());
        }
        LoginVo vo = new LoginVo();
        vo.setUser(user);
        vo.setToken(JwtUtil.createToken(user.getId()));
        return Result.ok(vo).message("登录成功");
    }


    /**
     * 用户信息修改
     * 可修改内容：用户名（username）、电话（phone）、邮箱（email）
     */
    @Override
    public Result userUpdate(UserInfo userInfo) {
        User user = getById(userInfo.getId());
        if(Optional.ofNullable(user).isEmpty()){
            return Result.fail().message("用户信息不存在");
        }
        user.setUsername(userInfo.getUsername());
        // TODO 校验手机号是否合法
        // 1. 手机号未被他人使用
        // 2. 符合国家标准
        user.setUserPhone(userInfo.getUserPhone());
        user.setEmail(userInfo.getEmail());
        user.setUpdateTime(LocalDateTime.now());
        updateById(user);
        user.setAccessCode("");
        return Result.ok(user).message("用户信息修改成功");
    }

    @Override
    public Result getUserList(UserDTO dto) {
        Integer userId = dto.getUserId();
        User admin = getById(userId);
        if(Optional.ofNullable(admin).isEmpty() || admin.getType() !=4) {
            return Result.fail().message("非超级管理员");
        }
        Page<User> page = new Page(dto.getCurrent(), dto.getSize());
        MPJLambdaWrapper<User> wrapper = new MPJLambdaWrapper<>();
        wrapper.selectAll(User.class)
                .select(Balance::getBalance)
                .leftJoin(Balance.class, Balance::getUserId, User::getId)
                .between(User::getType, 0, 2);
        page = page(page, wrapper);
        List<User> records = page.getRecords();
        records.forEach(item -> {
            // 对用户信息进行脱敏处理
            String phoneNumber = item.getUserPhone();
            if(Optional.ofNullable(phoneNumber).isEmpty() || phoneNumber.length() == 0){
                item.setUserPhone( "暂无用户联系方式");
            }else {
                if (phoneNumber.length() == 11){
                    item.setUserPhone(phoneNumber.substring(0, 3) + "****" + phoneNumber.substring(7));
                }
            }

            item.setEmail(PhoneUtil.maskEmail(item.getEmail()));
        });
        QueryVO<User> vo = new QueryVO<>();
        vo.setTotal(page.getTotal());
        vo.setList(records);
        return Result.ok(vo).message("ok");
    }

    @Override
    public Result getSchool(Integer userId) {
        School one = schoolService.getOne(Wrappers.<School>lambdaQuery()
                .eq(School::getUserId, userId));
        return Result.ok(one).message("ok");
    }

    @Override
    public Result userRecharge(Integer userId, Double amount) {
        Balance balance = balanceService.getOne(Wrappers.<Balance>lambdaQuery()
                .eq(Balance::getUserId, userId));
        if (Optional.ofNullable(balance).isEmpty()) {
            return Result.fail().message("未查询到账户信息");
        }
        Double aDouble = balance.getBalance();
        balance.setBalance(aDouble + amount);
        balanceService.updateById(balance);
        // 记录此次充值记录
        Payment payment = new Payment();
        payment.setUserId(userId);
        payment.setAmount(amount);
        payment.setSituation("账户余额充值");
        payment.setTime(LocalDateTime.now());
        paymentService.save(payment);
        return Result.ok().message("充值成功");
    }

    @Override
    public Result checkPhone(PhoneDTO dto) {
        User user = getById(dto.getUserId());
        if (Optional.ofNullable(user).isEmpty()) {
            return Result.fail().message("用户信息不存在");
        }

        // 校验手机号码是否正确
        if (!PhoneUtil.isValidPhoneNumber(dto.getPhone())) {
            return Result.fail().message("手机号格式错误");
        }
        // 简单校验
        if (dto.getPhone() == null){
            return Result.fail().message("手机号格式错误");
        }

        user.setUserPhone(dto.getPhone());
        user.setEmail(dto.getEmail());
        updateById(user);
        // 获取用户余额信息
        Double balance = balanceService.getOne(Wrappers.<Balance>lambdaQuery().eq(Balance::getUserId, user.getId())).getBalance();
        user.setBalance(balance);
        return Result.ok(user).message("手机号信息设置成功");
    }

    @Override
    public Result useNum() {
        Long count = getBaseMapper().selectCount(Wrappers.<User>lambdaQuery().in(User::getType, 0, 1, 2));
        return Result.ok(count).message("ok");
    }

    @Override
    public Result createAdmin(User user) {
        String phone = user.getUserPhone();
        if (!StringUtils.isNotBlank(phone)) {
            return Result.fail().message("管理员联系电话不能为空");
        }
        User one = getOne(Wrappers.<User>lambdaQuery().eq(User::getUserPhone, phone));
        if (Optional.ofNullable(one).isPresent()) {
            return Result.fail().message("该电话号码已经被使用");
        }
        // 生成随机密码
        user.setAccessCode(MD5Util.generateRandomString(16));
        user.setType(3); // 管理员
        user.setOpenId("ecjtu-admin");
        save(user);
        return Result.ok().message("管理员信息添加成功");
    }

    @Override
    public Result removeAdmin(Integer adminId) {
        Admin msg = adminMapper.selectOne(Wrappers.<Admin>lambdaQuery().eq(Admin::getUserId, adminId));
        if (Optional.ofNullable(msg).isPresent()) {
            return Result.fail().message("当前管理员已在管理场馆，请先进行取消");
        }
        removeById(adminId);
        return Result.ok().message("移除成功");
    }


    @Override
    public Result admininfo(PageDTO dto) {
        Page<User> page = new Page<>(dto.getCurrent(), dto.getSize());
        page = page(page, Wrappers.<User>lambdaQuery().eq(User::getType, 3));
        List<User> users = page.getRecords();
        users.forEach(item -> {
            // 对用户信息进行脱敏处理
            String phoneNumber = item.getUserPhone();
            if(Optional.ofNullable(phoneNumber).isEmpty() || phoneNumber.length() == 0){
                item.setUserPhone( "暂无用户联系方式");
            }else {
                if (phoneNumber.length() == 11){
                    item.setUserPhone(phoneNumber.substring(0, 3) + "****" + phoneNumber.substring(7));
                }
            }
            item.setEmail(PhoneUtil.maskEmail(item.getEmail()));
        });
        QueryVO<User> vo = new QueryVO<>();
        vo.setList(users);
        vo.setTotal(page.getTotal());
        return Result.ok(vo).message("查询成功");
    }

    @Override
    public Result passwd(PasswdDTO dto) {
        Integer userId = dto.getId();
        User user = getById(userId);
        String passwd = user.getAccessCode();
        if(!passwd.equals(dto.getOldPasswd())){
            return Result.fail().message("原密码输入错误");
        }
        String newPasswd = dto.getNewPasswd();
        if (passwd.equals(newPasswd)) {
            return Result.fail().message("新旧密码不能一致");
        }
        if (!newPasswd.equals(dto.getRePasswd())) {
            return Result.fail().message("两次输入密码不一致");
        }
        if (!MD5Util.validateString(newPasswd)) {
            return Result.fail().message("新密码至少长度大于10，同时至少包含大小写字符、数字和特殊字符");
        }
        user.setAccessCode(newPasswd);
        updateById(user);
        return Result.ok().message("密码修改成功");
    }

    @Override
    public Result upload(Integer userId, MultipartFile file) {
        User user = getById(userId);
        if (Optional.ofNullable(user).isEmpty()) {
            return Result.fail().message("用户信息不存在");
        }

        try {
            // 检查是否上传过头像
            if(user.getIsUpload() == 1) {
                try {
                    minioUtil.removeFile(user.getAvatar());
                } catch (Exception e) {
                    throw new MinioRemoveException("删除旧文件失败: " + e.getMessage());
                }
            }

            // 文件校验（修改为抛出异常）
            ImageValidator.validateImageFileWithException(file);

            String extension = FilenameUtils.getExtension(file.getOriginalFilename());
            String filename = UUID.randomUUID().toString().replaceAll("-", "") + "." + extension;

            try {
                minioUtil.uploadFile(file, filename, file.getContentType());
            } catch (Exception e) {
                // 上传失败时重置用户状态
                user.setIsUpload(0);
                updateById(user);
                throw new MinioUploadException("文件上传失败: " + e.getMessage());
            }

            // 更新用户信息
            user.setIsUpload(1);
            user.setAvatar(filename);
            updateById(user);
            return Result.ok(imgPrefix + user.getAvatar()).message("头像上传成功");

        } catch (MinioRemoveException | ImageValidateException | MinioUploadException e) {
            throw e; // 抛出给全局异常处理器
        } catch (Exception e) {
            log.error("系统未知错误: {}" + e.getMessage());
            return Result.fail().message("系统错误，请稍后重试");
        }
    }


    @Override
    public Result getUserInfo(Integer userId) {
        User user = getById(userId);
        Integer type = user.getType();
        if(type == 1 || type == 0){
            // 脱敏处理
            user.setUserPhone(PhoneUtil.maskPhone(user.getUserPhone()));
            user.setEmail(PhoneUtil.maskEmail(user.getEmail()));
        }
        user.setBalance(0.0);
        Balance balance = balanceService.getOne(Wrappers.<Balance>lambdaQuery().eq(Balance::getUserId, userId));
        if (Optional.ofNullable(balance).isPresent()) {
            user.setBalance(balance.getBalance());
        }
        if(StringUtils.isNotBlank(user.getAvatar())){
            user.setAvatar(imgPrefix + user.getAvatar());
        }
        user.setAccessCode("");
        return Result.ok(user).message("用户信息查询成功");
    }

    @Override
    public Result passwordReset(ResetDTO dto) {
        User user = getOne(Wrappers.<User>lambdaQuery().eq(User::getUserPhone, dto.getPhone()));
        if (Optional.ofNullable(user).isEmpty()) {
            return Result.fail().message("手机号未认证");
        }
        if (Optional.ofNullable(user.getEmail()).isEmpty()) {
            return Result.fail().message("邮箱未认证");
        }
        if (!user.getEmail().equals(dto.getEmail())) {
            return Result.fail().message("手机号信息与邮箱信息不匹配");
        }
        try{
            emailService.sendSimpleMessage(dto.getEmail(), user.getUserPhone() + "用户密码重置", PasswordGenerator.generatePassword());
        }catch (Exception e){
            return Result.fail().message("邮件发送失败，请稍后重新尝试");
        }
        return Result.ok().message("邮件发送成功");
    }
}




